
#include "as_session.h"
#include "byte_buffer.h"
#include <assert.h>

#define WRITE_BUF_INIT_SIZE 1024

namespace as
{
namespace inet
{
AsSession::AsSession()
{
    m_writeBuf = std::make_unique<ByteBuffer>();
    m_writeBuf->make_writeable_bytes(WRITE_BUF_INIT_SIZE);
}

AsSession::~AsSession()
{
    m_ioContextPool.DebugInfo();
}

void AsSession::reset()
{
    m_socket.release();
    m_ioType = IOType::NONE;
    m_sid = 0;
}

AsSession* AsSession::init()
{
    m_socket.release();
    socket_fd fd;
#ifdef _WIN32
    fd = WSASocketW(AF_INET, SOCK_STREAM, 0, nullptr, 0, WSA_FLAG_OVERLAPPED);
#else
    fd = ::socket(PF_INET, SOCK_STREAM, 0);
#endif
    assert(fd != SOCKET_ERR_FD);

    init(fd);
    return this;
}

AsSession* AsSession::init(socket_fd fd)
{
    if(m_socket == nullptr)
    {
        m_socket = std::make_unique<AsSocket>(fd);
    }
    else
    {
        m_socket->set_fd(fd);
    }

    return this;
}

AsSession* AsSession::listen(const NetAddr &addr)
{
    m_addr = addr;
    int ret = m_socket->bind(m_addr);
    if(ret == -1)
    {
        return nullptr;
    }

    ret = m_socket->listen();
    if(ret == -1)
    {
        return nullptr;
    }

    return this;
}

AsSession* AsSession::connect(const NetAddr &addr)
{
    m_addr = addr;
    int ret = m_socket->connect(m_addr);
    if(ret == -1)
    {
        std::cout << "error :" << AsSocket::get_last_err() << std::endl;
        return nullptr;
    }
    return this;
}

void AsSession::close()
{
    if(m_socket)
    {
        m_socket->close();
    }
}


#ifdef _WIN32
AsSession *AsSession::set_client_addr(SOCKADDR_IN *clientAddr)
{
    //memcpy_s(&(newSockContext->clientAddr), sizeof(SOCKADDR_IN), clientAddr, sizeof(SOCKADDR_IN));
    char buf[32] = { 0 };
	auto address = inet_ntop(AF_INET, &clientAddr->sin_addr, buf, sizeof(buf));
    m_addr.set_addr(address, 0);
    return this;
}

#else
AsSession* AsSession::set_client_addr(sockaddr_in *clientAddr)
{
    char buf[32] = { 0 };
	auto address = inet_ntop(AF_INET, &clientAddr->sin_addr, buf, sizeof(buf));
    m_addr.set_addr(address, 0);
    return this;
}
#endif

socket_fd AsSession::get_socket_fd()
{
    if(m_socket)
    {
        return m_socket->get_fd();
    }
    return SOCKET_ERR_FD;
}

int AsSession::send_packet(std::string_view buf)
{
    m_writeBuf->write_bytes((const uint8*)buf.data(), (int)buf.size());
    int ret = out_packet();
    return ret;
}

IOContext* AsSession::pop_io_context()
{
    return m_ioContextPool.pop();
}

void AsSession::push_io_context(IOContext *io)
{
    m_ioContextPool.push(io);
}

int AsSession::out_packet()
{
    std::lock_guard<std::mutex> l(m_writeLock);
#ifdef _WIN32
    IOContext *io = pop_io_context();
    io->type = IOType::Write;
    io->socket = m_socket->get_fd();

    DWORD sendbytes = 0;
	DWORD flags = 0;
    WSABUF wsbuf;
    wsbuf.buf = (CHAR*)(m_writeBuf->BeginWritePtr());
    wsbuf.len = m_writeBuf->GetWritePos();

    //LOG_INFO << "buffer->" << std::endl;
    for(int i = 0; i < (int)wsbuf.len; ++i)
    {
        //LOG_INFO << wsbuf.buf[i];
    }

    //LOG_INFO << std::endl;

    if(WSASend(m_socket->get_fd(), &wsbuf, 1, &sendbytes, flags, &io->overlapped, nullptr) == SOCKET_ERROR)
    {
        auto err = AsSocket::get_last_err(); 
        if(err != WSA_IO_PENDING)
        {
            
            //LOG_ERROR << "send packet error:" << err << std::endl;
            return -2;
        }
    }

#else
    uint8 *wsbuf = m_writeBuf->BeginWritePtr();

    LOG_INFO << "buffer->" << std::endl;
    m_writeBuf->printInfo();
    LOG_INFO << std::endl;

    int ret = send(m_socket->get_fd(), wsbuf, m_writeBuf->GetWritePos(), 0);
    if(ret < 0)
    {
        LOG_ERROR << "send error:" << errno << " errmsg:" << strerror(errno) << std::endl;
        m_socket->close();
        return -1;
    }

#endif

    return 0;
}

}
}

